package main

import (
	"fmt"

	"unsafe"

	"github.com/templexxx/reedsolomon"
)

/*
#cgo CFLAGS: -mavx -mavx2
#cgo LDFLAGS: -lm
#include <stdio.h>
#include <immintrin.h>
int *AVX4K(const int a[1024], const int b[1024],  int c[1024]){
    int m = 0xFFFFFFFF;
    __m256i loada;
    __m256i loadb;
    __m256i loadc;
    for (int i = 0; i < 32; i++){
        loada = _mm256_lddqu_si256((__m256i*)(a+32*i));
        loadb = _mm256_loadu_si256((__m256i*)(b+32*i));
        loadc = _mm256_xor_si256(loada,loadb);
        _mm256_storeu_si256((__m256i*)(c+32*i),loadc);

        loada = _mm256_loadu_si256((__m256i*)(a+32*i+8));
        loadb = _mm256_loadu_si256((__m256i*)(b+32*i+8));
        loadc = _mm256_xor_si256(loada,loadb);
        _mm256_storeu_si256((__m256i*)(c+32*i+8),loadc);

        loada = _mm256_loadu_si256((__m256i*)(a+32*i+16));
        loadb = _mm256_loadu_si256((__m256i*)(b+32*i+16));
        loadc = _mm256_xor_si256(loada,loadb);
        _mm256_storeu_si256((__m256i*)(c+32*i+16),loadc);

        loada = _mm256_loadu_si256((__m256i*)(a+32*i+24));
        loadb = _mm256_loadu_si256((__m256i*)(b+32*i+24));
        loadc = _mm256_xor_si256(loada,loadb);
        _mm256_storeu_si256((__m256i*)(c+32*i+24),loadc);
    }
    return 0;
}

int *AVX4M(const int a[1024*1024], const int b[1024*1024],  int c[1024*1024]){
    for(int i =0; i<1024; i++){
        AVX4K(a+i*1024,b+i*1024,c+i*1024);
    }
    return 0;
}
*/
import "C"

// func Xor4M(da [1024 * 1024]int32, db [1024 * 1024]int32, dc [1024 * 1024]int32) {
// 	a0 := (*C.int)(unsafe.Pointer(&da[0]))
// 	b0 := (*C.int)(unsafe.Pointer(&db[0]))
// 	c0 := (*C.int)(unsafe.Pointer(&dc[0]))
// 	C.AVX4M(a0, b0, c0)
// }

func Xor4M(da []byte, db []byte, dc []byte) {
	a0 := (*C.int)(unsafe.Pointer(&da[0]))
	b0 := (*C.int)(unsafe.Pointer(&db[0]))
	c0 := (*C.int)(unsafe.Pointer(&dc[0]))
	C.AVX4M(a0, b0, c0)
}

func Xor4K(da []byte, db []byte, dc []byte) {
	a0 := (*C.int)(unsafe.Pointer(&da[0]))
	b0 := (*C.int)(unsafe.Pointer(&db[0]))
	c0 := (*C.int)(unsafe.Pointer(&dc[0]))
	C.AVX4K(a0, b0, c0)
}

func ErasureEncodeXor(d int, p int, data [][]byte) error {
	if d+p != len(data) {
		return fmt.Errorf("cluster [%d] -> data length error: d: %d, p: %d, length data: %d", *ClusterID, d, p, len(data))
	}
	for i := 1; i < d; i++ {
		if len(data[0]) != len(data[i]) {
			return fmt.Errorf("cluster [%d] -> length of origin data not equal", *ClusterID)
		}
	}
	if d == 3 {
		dataTmp := make([]byte, len(data[0]))
		data[3] = make([]byte, len(data[0]))
		Xor4M(data[0], data[1], dataTmp)
		Xor4M(data[2], dataTmp, data[3])
	} else if d == 2 {
		data[2] = make([]byte, len(data[0]))
		Xor4M(data[0], data[1], data[2])
	} else if d == 1 {
		data[1] = data[0]
	} else {
		return fmt.Errorf("cluster [%d] -> invalid d", *ClusterID)
	}
	return nil
}

func ErasureEncode(d int, p int, data [][]byte) error {
	if d+p != len(data) {
		return fmt.Errorf("data length error: d: %d, p: %d, length data: %d", d, p, len(data))
	}
	for i := 1; i < d; i++ {
		if len(data[0]) != len(data[i]) {
			return fmt.Errorf("cluster [%d] -> length of origin data not equal", *ClusterID)
		}
	}
	for i := 0; i < p; i++ {
		data[i+d] = make([]byte, len(data[0]))
	}
	es, err := reedsolomon.New(d, p)
	if err != nil {
		return err
	}
	err = es.Encode(data)
	if err != nil {
		return err
	}
	return nil
}

func ErasureReconst(d int, p int, data [][]byte, need []int) error {
	if d+p != len(data) {
		return fmt.Errorf("data length error: d: %d, p: %d, length data: %d", d, p, len(data))
	}
	for i := 1; i < d+p; i++ {
		if len(data[0]) != len(data[i]) {
			return fmt.Errorf("cluster [%d] -> length of data not equal", *ClusterID)
		}
	}
	var dphas []int
	for i := 0; i < d+p; i++ {
		if i != need[0] {
			dphas = append(dphas, i)
		}
	}
	es, err := reedsolomon.New(d, p)
	if err != nil {
		return err
	}
	err = es.Reconst(data, dphas, need)
	if err != nil {
		return err
	}
	return nil
}
